﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2013  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "GLESRenderState.h"
#include "GLESStateManager.h"

namespace UX_STL
{
namespace GLESshell
{


	inline bool ValidateBlendFunc(gl_blend_func::type &eType)
	{
		switch ( eType )
		{
			case gl_blend_func::ZERO :
			case gl_blend_func::ONE :
			case gl_blend_func::SRC_COLOR :
			case gl_blend_func::ONE_MINUS_SRC_COLOR :
			case gl_blend_func::DST_COLOR :
			case gl_blend_func::ONE_MINUS_DST_COLOR :
			case gl_blend_func::SRC_ALPHA :
			case gl_blend_func::ONE_MINUS_SRC_ALPHA :
			case gl_blend_func::DST_ALPHA :
			case gl_blend_func::ONE_MINUS_DST_ALPHA :
			case gl_blend_func::CONST_COLOR :
			case gl_blend_func::ONE_MINUS_CONST_COLOR :
			case gl_blend_func::CONST_ALPHA :
			case gl_blend_func::ONE_MINUS_CONST_ALPHA :
			case gl_blend_func::SRC_ALPHA_SATURATE :
			//case gl_blend_func::SRC1_COLOR :
			//case gl_blend_func::SRC1_ALPHA :
			//case gl_blend_func::ONE_MINUS_SRC1_COLOR :
			//case gl_blend_func::ONE_MINUS_SRC1_ALPHA :
				return true;
		};
		eType = gl_blend_func::_INIT;
		return false;
	}

	inline bool ValidateBlendEquation(gl_blend_equation::type &eType)
	{
		switch ( eType )
		{
			case gl_blend_equation::ADD :
			case gl_blend_equation::SUB :
			case gl_blend_equation::REV_SUB :
			case gl_blend_equation::MIN :
			case gl_blend_equation::MAX :
				return true;
		};
		eType = gl_blend_equation::_INIT;
		return false;
	}

	inline bool ValidateCompareMode(gl_compare::type &eType)
	{
		switch ( eType )
		{
			case gl_compare::NEVER :
			case gl_compare::LESS :
			case gl_compare::EQUAL :
			case gl_compare::LEQUAL :
			case gl_compare::GREATER :
			case gl_compare::NOTEQUAL :
			case gl_compare::GEQUAL :
			case gl_compare::ALWAYS :
				return true;
		};
		eType = gl_compare::_INIT;
		return false;
	}

	inline bool ValidateStencilOp(gl_stencil_op::type &eType)
	{
		switch ( eType )
		{
			case gl_stencil_op::DECR :
			case gl_stencil_op::DECR_WRAP :
			case gl_stencil_op::INCR :
			case gl_stencil_op::INCR_WRAP :
			case gl_stencil_op::INVERT :
			case gl_stencil_op::KEEP :
			case gl_stencil_op::REPLACE :
			case gl_stencil_op::ZERO :
				return true;
		};
		eType = gl_stencil_op::_INIT;
		return false;
	}



//------------------------ GLRenderState --------------------------//
/*
=================================================
	установить состояния рендера
=================================================
*
	void GLRenderState::Apply() const
	{
		const uint	u_max_buffers	= C_GL_MAX_COLOR_ATTACHMENTS;

		// Color //
		FOR( i, aColorBuffers )
		{
			const uint				u_buf	= aColorBuffers[i].uIndex;
			const TColorBuffer &	cb		= aColorBuffers[i];

			if ( u_buf >= u_max_buffers )
				continue;
			
			SMAN()->ColorMaski( u_buf, cb.eMask );
			SMAN()->BlendState( u_buf, cb.bBlending );

			if ( cb.bBlending )
			{
				SMAN()->BlendEquationSeparatei( u_buf, cb.eModeRGB, cb.eModeAlpha );
				SMAN()->BlendFuncSeparatei( u_buf, cb.eSrcRGB, cb.eSrcAlpha, cb.eDstRGB, cb.eDstAlpha );
			}
		}


		// Culling //
		if ( eCullMode != e_cull_mode::NONE )
		{
			SMAN()->SetState( e_gl_state::CULL_FACE, EnumNoCmp(eCullMode, e_cull_mode::_CULL_MASK) );
			
			if ( EnumCmpE( eCullMode, e_cull_mode::BACK ) )
				SMAN()->CullFace( gl_face::BACK );
			else
			if ( EnumCmpE( eCullMode, e_cull_mode::FRONT ) )
				SMAN()->CullFace( gl_face::FRONT );
			else
			if ( EnumCmpE( eCullMode, e_cull_mode::FRONT_AND_BACK ) )
				SMAN()->CullFace( gl_face::FRONT_AND_BACK );

			if ( EnumCmpE( eCullMode, e_cull_mode::FACE_CCW ) )
				SMAN()->FrontFace( gl_front_face::CCW );
			else
				SMAN()->FrontFace( gl_front_face::CW );
		}
		else
			SMAN()->Disable( e_gl_state::CULL_FACE );


		// Depth //
		SMAN()->DepthFunc( sDepth.eFunc );
		SMAN()->DepthMask( sDepth.bWrite );
		SMAN()->SetState( e_gl_state::DEPTH_TEST, sDepth.bTest );


		// Stencil //
		if ( sStencil.bTest )
		{
			const TStencilState::TSide &	front	= sStencil.aSide[0];
			const TStencilState::TSide &	back	= sStencil.aSide[1];

			SMAN()->Enable( e_gl_state::STENCIL_TEST );
			SMAN()->StencilFuncSeparate( gl_face::BACK, back.eFunc, back.uFuncRef, back.uFuncMask );
			SMAN()->StencilFuncSeparate( gl_face::FRONT, front.eFunc, front.uFuncRef, front.uFuncMask );
			SMAN()->StencilMaskSeparate( gl_face::BACK, back.uMask );
			SMAN()->StencilMaskSeparate( gl_face::FRONT, front.uMask );
			SMAN()->StencilOpSeparate( gl_face::BACK, back.eSFail, back.eDFail, back.eDPPass );
			SMAN()->StencilOpSeparate( gl_face::FRONT, front.eSFail, front.eDFail, front.eDPPass );
		}
		else
			SMAN()->Disable( e_gl_state::STENCIL_TEST );

		// States //
		SMAN()->PolygonMode( ePolygon );
		
		switch ( ePolygon )
		{
			case gl_polygon_mode::FILL :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_FILL,	sStates.bPolygonOffset );
				SMAN()->SetState( e_gl_state::POLYGON_SMOOTH,		sStates.bSmooth );
				break;

			case gl_polygon_mode::LINE :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_LINE,	sStates.bPolygonOffset );
				SMAN()->SetState( e_gl_state::LINE_SMOOTH,			sStates.bSmooth );
				break;

			case gl_polygon_mode::POINT :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_POINT,	sStates.bPolygonOffset );
				break;
		};

		if ( sStates.bPolygonOffset )
			GL_CALL( glPolygonOffset( vPolygonOffset.x, vPolygonOffset.y ) );
		
		SMAN()->SetState( e_gl_state::DEPTH_CLAMP,					sStates.bDepthClamp );
		SMAN()->SetState( e_gl_state::DITHER,						sStates.bDither );
		SMAN()->SetState( e_gl_state::PROGRAM_POINT_SIZE,			sStates.bProgramPointSize );
		SMAN()->SetState( e_gl_state::TEXTURE_CUBE_MAP_SEAMLESS,		sStates.bCubemapSeamless );
		SMAN()->SetState( e_gl_state::FRAMEBUFFER_SRGB,				sStates.bFramebufferSRGB );
	}
	
/*
=================================================
	испавление некорректных значений
=================================================
*/
	void GLESRenderState::Validate()
	{
		 TColorBuffer &	cb		= sColorBuffer;

		if ( !ValidateBlendFunc( cb.eSrcRGB ) )			cb.eSrcRGB		= gl_blend_func::ONE;
		if ( !ValidateBlendFunc( cb.eSrcAlpha ) )		cb.eSrcAlpha	= cb.eSrcRGB;
		if ( !ValidateBlendFunc( cb.eDstRGB ) )			cb.eDstRGB		= gl_blend_func::ZERO;
		if ( !ValidateBlendFunc( cb.eDstAlpha ) )		cb.eDstAlpha	= cb.eDstAlpha;

		if ( !ValidateBlendEquation( cb.eModeRGB ) )	cb.eModeRGB		= gl_blend_equation::ADD;
		if ( !ValidateBlendEquation( cb.eModeAlpha ) )	cb.eModeAlpha	= cb.eModeRGB;
		

		if ( !ValidateCompareMode( sDepth.eFunc ) )		sDepth.eFunc	= gl_compare::LEQUAL;

		if ( !ValidateCompareMode( sStencil.aSide[0].eFunc ) )
			sStencil.aSide[0].eFunc	= gl_compare::ALWAYS;

		if ( !ValidateCompareMode( sStencil.aSide[1].eFunc ) )
			sStencil.aSide[1].eFunc	= gl_compare::ALWAYS;

		ValidateStencilOp( sStencil.aSide[0].eSFail );
		ValidateStencilOp( sStencil.aSide[0].eDFail );
		ValidateStencilOp( sStencil.aSide[0].eDPPass );
		ValidateStencilOp( sStencil.aSide[1].eSFail );
		ValidateStencilOp( sStencil.aSide[1].eDFail );
		ValidateStencilOp( sStencil.aSide[1].eDPPass );
	}
	
//-------------------------------------------------------------------

}	// GLESshell
}	// UX_STL